from django.db import models
from django.db.models import F, Q
from django.db.models import Avg, Sum, Max, Min


# 需求：自定义管理器对象
# 1.自定义管理器类，继承于models.Manager
class BookinfoManager(models.Manager):

    # 2.在自定义的管理器类中重写或者自定义方法，完成对应需求

    # 2.1 重写all()方法修改其功能为：返回id小于3的所有数据
    def all(self):

        # BookInfo.objects.filter()
        return super().filter(id__lt=3)

    # 2.2 给书籍类提供一个创建书籍对象的方法
    def add_book(self, title, date):

        # 1.创建书籍对象
        book = BookInfo()
        # 2.给各个属性赋值
        book.btitle = title
        book.bpub_date = date
        # 3.保存到数据库，并返回书籍对象
        book.save()
        return book


# Create your models here.
# ORM: 定义模型类 ---> 关系型数据库表
# 需求：书籍[一] <---> 英雄[多]
# 需要继承： Model
class BookInfo(models.Model):

    # 3.在模型类中添加管理器对象
    # 一旦自定义管理器对象 系统提供的objects对象就失效了
    books = BookinfoManager()

    # 类属性 最终转换成表中的字段
    btitle = models.CharField(max_length=20, verbose_name="书籍名称")
    bpub_date = models.DateField(verbose_name="出版日期")
    bread = models.IntegerField(default=0,verbose_name="阅读量")
    bcomment = models.IntegerField(default=0, verbose_name="评论量")
    is_delete = models.BooleanField(default=False, verbose_name='逻辑删除')

    # 添加图片字段
    # upload_to: 指明图片具体保存到media下那个文件夹
    # null=True: 图片可以为空
    # 注意：新增修改字段需要重写进行数据库迁移
    image = models.ImageField(upload_to="pic", null=True, verbose_name="书籍图片")

    # 元类  --> type
    class Meta:
        # 指明数据库的表的名称 如果没有指明默认使用：子应用名小写_模型类名小写作为表的名称
        db_table = "tb_books"
        verbose_name = "图书"
        verbose_name_plural = verbose_name

    def __str__(self):
        # 格式化输出
        # print(book) --- <classs:BookInfo >
        # print(book) --- 三国演义
        return self.btitle

    # 自定义方法，修改书籍列表页，bpub_date的展示格式
    def my_date(self):
        # 将日期各种按照strftime参数进行转换成字符串
        return self.bpub_date.strftime('%Y年-%m月-%d日')

    my_date.short_description = "自定义日期"
    # 注意点: 排序字段必须是数据库的真实字段
    my_date.admin_order_field = "bpub_date"



# 定义英雄模型类HeroInfo
class HeroInfo(models.Model):

    # 相当于枚举数据类型
    GENDER_CHOICES = (
        (0, 'female'),
        (1, 'male')
    )

    hname = models.CharField(max_length=20, verbose_name='英雄名称')
    hgender = models.SmallIntegerField(choices=GENDER_CHOICES, default=0, verbose_name='性别')
    hcomment = models.CharField(max_length=200, null=True, verbose_name='描述信息')
    # 在多的一方定义外键形成关联
    # 删除外键关系 on_delete=models.CASCADE 外键设置-级联
    hbook = models.ForeignKey(BookInfo, on_delete=models.CASCADE, verbose_name='图书')  # 外键
    is_delete = models.BooleanField(default=False, verbose_name='逻辑删除')

    class Meta:
        db_table = 'tb_heros'
        verbose_name = '英雄'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.hname

    # 需求：在英雄列表页面展示图书的阅读量 【关联展示】
    def book_read(self):
        # 返回 英雄对应 书籍的 阅读量
        # hero.hbook.bread
        return self.hbook.bread

    book_read.short_description = "书籍阅读量"

    # book_read.admin_order_field =


"""
#1.查询编号为1的图书。

 BookInfo.objects.get(id=1)
 BookInfo.objects.get(id__exact=1)

#2.查询书名包含'传'的图书。contains
 BookInfo.objects.filter(btitle__contains='传')


#3.查询书名以'部'结尾的图书 endswith startswith

BookInfo.objects.filter(btitle__endswith='部')


#4.查询书名不为空的图书。 isnull = False

BookInfo.objects.filter(btitle__isnull=False)


#5.查询编号为1或3或5的图书  in

BookInfo.objects.filter(id__in=[1,3,5])

#6.查询编号大于3的图书

BookInfo.objects.filter(id__gt=3)

#7.查询编号不等于3的图书 exclude

BookInfo.objects.exclude(id=3)


#8.查询1980年发表的图书。
BookInfo.objects.filter(bpub_date__year=1980)
    
#9.查询1980年1月1日后发表的图书。

BookInfo.objects.filter(bpub_date__gt=date(1980,1,1))

------------------------------------------------------------------------------------
------------------------------------------------------------------------------------

前提导入模块: from django.db.models import F, Q


#1.查询 阅读量 大于等于 评论量 的图书

BookInfo.objects.filter(bread__gte=F('bcomment'))


    
#2.查询 阅读量 大于2倍 评论量的 图书

BookInfo.objects.filter(bread__gte=F('bcomment') * 2 )


#3.查询 阅读量大于20，并且 编号小于3的 图书

BookInfo.objects.filter(bread__gt=20, id__lt=3)

BookInfo.objects.filter(Q(bread__gt=20) &  Q(id__lt=3) )

#4.查询阅读量大于20，或 编号小于3的图书，只能使用Q对象实现

 BookInfo.objects.filter(Q(bread__gt=20) |  Q(id__lt=3) )

#5.查询编号不等于3的图书 exclude

BookInfo.objects.filter(~Q(id=3))



------------------------------------------------------------------------------------
---------------------------------关联查询---------------------------------------------

书籍 [一] <---> 英雄 [多]

#1. 一查多 语法：一对应的模型类对象.多对应的模型类名小写_set
	book.heroinfo_set.all()
    
案列：查询天龙八部中所有英雄

    # 1.获取天龙八部书籍对象
    book1 = BookInfo.objects.get(btitle="天龙八部")
    
    # 2.根据对象进行关联查询
    book1.heroinfo_set.all()


#2. 多查一 语法: 多对应的模型类对象.外键属性名称
		hero.hbook

案列：查询令狐冲英雄属于哪个本书籍 

    # 1.获取令狐冲英雄对象
    h1 = HeroInfo.objects.get(hname="令狐冲")
    
    # 2.根据英雄对象的外键查询书籍对象
    h1.hbook : 该英雄所对应的书籍
    h1.hbook_id: 该英雄所对应的书籍的id
    

------------------------------------------------------------------------------------
---------------------------------关联过滤查询-----------------------------------------
    
书籍 [一] <---> 英雄 [多]


#《一》多模型类条件 查询 一模型类数据: 多查一

语法：关联模型类名小写__属性名__条件运算符=值
     heroinfo__hname__exact="东方不败"

# 案列：查询图书，要求图书英雄为"东方不败"

BookInfo.objects.filter(heroinfo__hname__exact="东方不败")



#《二》 一模型类条件 查询 多模型类数据 : 一查多


语法：外键名称__一模型类属性名__条件运算符=值

    hbook__btitle__exact="天龙八部"

# 案列：查询 书名为“天龙八部”的所有 英雄

    HeroInfo.objects.filter(hbook__btitle__exact="天龙八部")
    

# 案列： 询图 书阅读量大于30的所有 英雄

    HeroInfo.objects.filter(hbook__bread__gt=30)

    
"""